<?php
require_once(DIR_LIBRARY."orm".DS."orm.php");
require_once(DIR_LIBRARY."orm".DS."acl".DS."permission.php");
class AclAccess extends ORM
{

    const TYPE_MENU = 1;
    const TYPE_ACTION = 2;

    protected $dsn = AclPermission::DSN;
    protected $tablename = "access";

    private static $instance;

    public static $types = array(
        self::TYPE_MENU => "菜单", 
        self::TYPE_ACTION => "操作点",
    );

    public function __construct($id = NULL)
    {
        parent::__construct($id);
    }

    public function getAll($tree = true)
    {
        $this->appendWhere(1);
        $rs = $this->execute();
        
        if (!$rs->isSuccess())
        {
            throw new Exception("db query fail");
        }
        
        $rows = array();
        while ($row = $rs->fetchRow())
        {
            if ($tree)
            {
                if ($row["parent_id"])
                {
                    $rows[$row["parent_id"]]["child"][$row["id"]] = $row;
                }
                else
                {
                    $rows[$row["id"]] = $row;
                }
            }
            else
            {
                $rows[$row["id"]] = $row;
            }
        }

        return $rows;    
    }


    public static function getAllMenu($tree = true)
    {
        $access = self::factory();
        $access->appendWhere("type_id=".self::TYPE_MENU);
        $access->appendWhere("level <= 2 ORDER BY id ASC");
        $rs = $access->execute();
        
        if (!$rs->isSuccess())
        {
            throw new Exception("db query fail");
        }
        
        $rows = array();
        while ($row = $rs->fetchRow())
        {
            if ($tree)
            {
                if ($row["parent_id"])
                {
                    $rows[$row["parent_id"]]["child"][$row["id"]] = $row;
                }
                else
                {
                    $rows[$row["id"]] = $row;
                }
            }
            else
            {
                $rows[$row["id"]] = $row;
            }
        }

        return $rows;    
    }

    public static function getUserMenuByAccessIds($access_ids, $tree = true)
    {
        $access = self::factory();
        self::buildMenuCondition($access);
        $access->appendWhere('id in ('.implode(",", $access_ids).")"); 

        $rs = $access->execute();

        if (!$rs->isSuccess())
        {
            throw new Exception("db query fail");
        }
        
        return self::formatMenuResult($rs, $tree);
    }


    private static function buildMenuCondition($access)
    {
        $access->appendWhere("type_id=".AclAccess::TYPE_MENU);
        $access->appendWhere("level <= 2");
    }

    private static function formatMenuResult($rs, $tree = true)
    {
        $rows = array();
        while ($row = $rs->fetchRow())
        {
            if ($tree)
            {
                if ($row["parent_id"])
                {
                    $rows[$row["parent_id"]]["child"][$row["id"]] = $row;
                }
                else
                {
                    $rows[$row["id"]] = $row;
                }
            }
            else
            {
                $rows[$row["id"]] = $row;
            }
        }

        return $rows;
    }

    public function formatMenu()
    {
        $rows = array();

        //while($row = $rs->fetchRow())
        $arr = array(
        0 => array("id" => 1, "parent_id" => 0, "name" => "read"),
        1 => array("id" => 2, "parent_id" => 1, "name" => "write"),
        2 => array("id" => 3, "parent_id" => 1, "name" => "delete"),
        3 => array("id" => 4, "parent_id" => 0, "name" => "aaa"),
        );
        foreach ($arr as $row)
        {
            $id = $row["id"];
            $parent_id = $row["parent_id"];

            if (0 == $parent_id)
            {
                if (isset($rows[$id]["child"]))
                {
                    $child = $rows[$id]["child"];
                    $rows[$id] = $row;
                    $rows[$id]["child"] = $child;
                }
                else
                {
                    $rows[$id] = $row;
                }
            }
            else
            {
                $rows[$parent_id]["child"][$id] = $row;
            }
        }

        return $rows;
    }

    public function create()
    {
        if (!$this->getName())
        {
            throw new Exception("must have name");
        }

        $type_id = (int)$this->getField("type_id");
        if (!in_array($type_id, array_keys(self::$types)))
        {
            throw new Exception("type id is invalid");
        }

        //if type is action ,have no parent,so parent_id set to 0
        if (self::TYPE_ACTION == $type_id)
        {
            if (NULL !== $this->getField("parent_id"))
            {
                throw new Exception("action can not have parent id");
            }
        }
        else
        {
            if (NULL === $this->getField("parent_id"))
            {
                throw new Exception("not have parent id");
            }
        }

        $this->setCommon($this->getField("parent_id"));

        $max_id = self::factory()->getMaxId();

        if (!$max_id)
        {
            $id = 1;
        }
        else
        {
            $id = $max_id * 2;
        }
        $this->setId($id);
        $this->setSQLType("insert");

        return parent::create();
    }

    public function update($id)
    {
        if (!$id)
        {
            throw new Exception("no id for update");
        }

        if (NULL !== $this->getName() && !$this->getName())
        {
            throw new Exception("must have name");
        }

        //can not change type id
        if (NULL !== $this->getField("type_id"))
        {
            throw new Exception("can not change type id");
        }

        $access = self::factory($id);

        $old_orderlist = $access->getField("order_list");
        $old_parent_id = $access->getField("parent_id");
        unset($access);

        $parent_id = $this->getField("parent_id");
        $this->setCommon($parent_id, $parent_access);
        
        //change child
        if ($parent_id != $old_parent_id)
        {
            $old_orderlist_slice = AclPermission::resolveList($old_orderlist);
            $where_str = "";
            foreach ($old_orderlist_slice as $v)
            {
                $where_str .= "order_list & ".$v."=".$v." AND ";  
            }

            $where_str .= " order_list & ".$id."=".$id;

            $child_old_orderlist = AclPermission::appendList($old_orderlist, $id);
            $child_new_orderlist = AclPermission::appendList($this->getField("order_list"), $id);

            $changed_access = self::factory();
            $changed_access->setField("order_list", "order_list-".$child_old_orderlist."+".$child_new_orderlist);
            $changed_access->appendWhere($where_str);
            $bool = parent::update($id);
        }

        return parent::update();
    }

    private function setCommon($parent_id, &$parent_access = NULL)
    {
        if (0 != $parent_id)
        {
            $parent_access = self::factory($parent_id);

            if (!$parent_access->isLoad())
            {
                throw new Exception("parent is not exists");
            }

            $parent_order_list = $parent_access->getField("order_list");
            $this->setField("order_list", AclPermission::appendList($parent_order_list, $parent_id));
            $this->setLevel($parent_access->getLevel() + 1);
        }
        else
        {
            $this->setLevel(1);
            $this->setField("order_list", 0);
        }
    }

    public static function factory($id = NULL)
    {
        return new self($id);
    }

    public static function getInstance()
    {
        if (!self::$instance)
        {
            self::$instance = new self();
        }

        return self::$instance;
    }
} //end class
